package processScheduling;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

/**
 * @author AnonyEast
 */
public class Scheduling {
    //进程列表(就绪队列)
    private static ArrayList<PCB> processList = new ArrayList<>();

    public static void main(String[] args) {
        System.out.println("****** 开始录入进程 ******");
        System.out.print("输入进程总数：");
        //存入总进程数
        Scanner sc = new Scanner(System.in);
        int countProcess = sc.nextInt();
        //输入所有进程信息，添加进程到进程列表
        inputProcessInfo(countProcess);
        System.out.println("\n****** 开始调度 ******\n");
        //打印所有进程
        System.out.println("****** 当前就绪队列 ******");
        for (PCB pcb : processList) {
            displayProcess(pcb);
        }
        System.out.println();

        //初始时间
        int h = 0;

        //当进程列表中存在进程时
        while (processList.size() != 0) {
            h++;
            System.out.println("****** 当前CPU时间:" + h);
            processList.get(0).setState("run");
            check(processList.get(0));
            running(processList.get(0));
            System.out.println("****** 输入任意值，并按回车键继续运行 ******");
            sc.next();
        }
        System.out.println("****** 进程全部运行完成 ******");
    }

    /**
     * 进程输入方法，将输入的进程放在进程列表中
     *
     * @param countProcess 进程数量
     */
    private static void inputProcessInfo(int countProcess) {
        Scanner sc = new Scanner(System.in);
        //填充列表
        for (int i = 0; i < countProcess; i++) {
            System.out.print("输入进程" + i + "的名称:");
            String name = sc.next();
            System.out.print("输入进程" + i + "的优先级:");
            int priority = sc.nextInt();
            System.out.print("输入进程" + i + "的需要运行时间:");
            int ntime = sc.nextInt();

            //初始化当前进程的PCB
            PCB pcb = new PCB(name, "ready", priority, ntime, 0);
            //加入就绪队列
            processList.add(pcb);
        }
        //对进程列表中的进程按优先级从小到大排序
        sort(processList);
        //反转进程列表，顺序变为优先级从大到小
        Collections.reverse(processList);
    }

    /**
     * 对进程列表按优先级从小到大排序的方法，此处使用选择排序
     *
     * @param processList 进程列表
     */
    private static void sort(ArrayList<PCB> processList) {
        //选择排序大法
        for (int tianHuaBan = processList.size() - 1; tianHuaBan >= 0; tianHuaBan--) {
            int indexOfMax = 0;//初始化最大优先级下标
            int max = processList.get(0).getPriority();//初始化最大优先级
            for (int i = 1; i <= tianHuaBan; i++) {
                if (processList.get(i).getPriority() > max) {
                    indexOfMax = i;
                    max = processList.get(i).getPriority();
                }
            }
            //退出内层for循环后，已经找到了最大的优先级，放到最后
            Collections.swap(processList, indexOfMax, tianHuaBan);
        }
    }

    /**
     * 进程销毁方法(进程运行结束,将进程从进程列表移除)
     */
    private static void destroy() {
        processList.remove(0);
    }

    /**
     * 查看当前正在运行的进程和打印就绪队列的方法
     *
     * @param pcb 进程PCB对象
     */
    private static void check(PCB pcb) {
        //显示正在运行的进程
        System.out.println("****** 当前正在运行的进程:" + pcb.getName());
        System.out.println();

        //显示就绪进程
        System.out.println("****** 当前就绪队列 ******");
        if (processList.size() == 0) {
            System.out.println("就绪队列为空");
        } else {
            for (PCB value : processList) {
                displayProcess(value);
            }
        }
        System.out.println();
    }

    /**
     * 进程运行方法，并且当进程运行时间到后,将该进程修改为就绪态
     *
     * @param pcb 进程PCB对象
     */
    private static void running(PCB pcb) {
        pcb.setRtime(pcb.getRtime() + 1);
        if (pcb.getRtime() == pcb.getNtime()) {
            destroy();
            System.out.println("****** " + pcb.getName() + "运行完毕，已删除该进程。" + " ******");
            System.out.println();
        } else {
            pcb.setPriority(pcb.getPriority() - 1);//优先级-1
            pcb.setState("ready");//状态修改为就绪态
            //重新排列进程列表
            sort(processList);
            //反转
            Collections.reverse(processList);
        }
        //打印就绪队列
        displayProcessList();
        System.out.println();
    }


    /**
     * 打印就绪队列方法
     */
    private static void displayProcessList() {
        System.out.println("****** 运行完一个时间片之后的就绪队列 ******");
        if (processList.size() == 0) {
            System.out.println("就绪队列为空");
        } else {
            for (PCB pcb : processList) {
                displayProcess(pcb);
            }
        }
    }

    /**
     * 进程打印方法
     *
     * @param pcb 进程PCB对象
     */
    private static void displayProcess(PCB pcb) {
        System.out.println(pcb.getName() + " 状态" + pcb.getState() + " 优先级" + pcb.getPriority() + " 需要运行时间" + pcb.getNtime() + " 已用CPU时间" + pcb.getRtime());
    }
}